package com.synergy.stockflow.aspects;

import com.synergy.stockflow.annotation.Log;
import com.synergy.stockflow.annotation.LogModule;
import com.synergy.stockflow.constant.ResultCode;
import com.synergy.stockflow.entity.SysLog;
import com.synergy.stockflow.entity.vo.SysUserVO;
import com.synergy.stockflow.exception.BusinessException;
import com.synergy.stockflow.service.SysLogService;
import com.synergy.stockflow.service.SysUserService;
import com.synergy.stockflow.utils.JsonUtils;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Date;

@Aspect
@Component
@Log4j2
public class LogAspect {

    private final SysLogService sysLogService;
    private final SysUserService sysUserService;
    private static final String UNKNOWN = "unknown";

    public LogAspect(SysLogService sysLogService, SysUserService sysUserService) {
        this.sysLogService = sysLogService;
        this.sysUserService = sysUserService;
    }

    @Around("@annotation(log8)")
    public Object around(ProceedingJoinPoint joinPoint, Log log8) throws Throwable {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();

        // 1. 检查是否需要忽略日志
        if (shouldSkipLogging(method, log8)) {
            return joinPoint.proceed();
        }

        // 2. 创建日志对象并填充基础信息
        SysLog sysLog = buildSysLog(joinPoint, method, log8);

        // 3. 设置开始时间
        sysLog.setStartTime(new Date());

        Object result;
        try {
            result = joinPoint.proceed();
            handleSuccess(sysLog, result, log8);
            return result;
        } catch (Exception e) {
            handleError(sysLog, e);
            throw e;
        } catch (Throwable t) {
            handleError(sysLog, new BusinessException(ResultCode.ERROR, t));
            throw t;
        } finally {
            finalizeAndSaveLog(sysLog);
        }
    }

    private HttpServletRequest getRequest() {
        try {
            return ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
        } catch (Exception e) {
            return null;
        }
    }

    private String getClientIP(HttpServletRequest request) {
        if (request == null) return null;
        String ip = request.getHeader("X-Forwarded-For");
        if (ip == null || ip.isEmpty() || UNKNOWN.equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.isEmpty() || UNKNOWN.equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.isEmpty() || UNKNOWN.equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        return ip.split(",")[0].trim();
    }

    private boolean shouldSkipLogging(Method method, Log log8) {
        LogModule module = method.getDeclaringClass().getAnnotation(LogModule.class);
        boolean isModuleEnabled = module != null && !module.disable();
        return !isModuleEnabled && log8.ignore();
    }

    private SysLog buildSysLog(ProceedingJoinPoint joinPoint, Method method, Log log8) {
        SysLog sysLog = new SysLog();
        sysLog.setTitle(log8.title());

        // 日志类型：优先使用方法上的，否则用模块的
        LogModule module = method.getDeclaringClass().getAnnotation(LogModule.class);
        String moduleType = module != null ? module.type() : "OPERATION";
        sysLog.setLogType(!log8.logType().isEmpty() ? log8.logType() : moduleType);

        // 操作描述
        if (module != null) {
            sysLog.setOperation(module.name() + " - " + log8.title());
        } else {
            sysLog.setOperation(log8.title());
        }

        // 请求信息
        HttpServletRequest request = getRequest();
        if (request != null) {
            sysLog.setRequestUrl(request.getRequestURI());
            sysLog.setIp(getClientIP(request));
            sysLog.setRequestMethod(request.getMethod());
        }

        // 方法全名
        sysLog.setMethod(method.getDeclaringClass().getName() + "." + method.getName());

        // 用户信息
        SysUserVO user = sysUserService.currentUser();
        if (user != null) {
            sysLog.setUserId(user.getId());
            sysLog.setUsername(user.getUsername());
        }

        // 参数记录
        if (log8.logParams()) {
            Object[] args = joinPoint.getArgs();
            if (args.length > 0) {
                String params = JsonUtils.toJson(args);
                sysLog.setParams(truncate(params));
            }
        }

        return sysLog;
    }

    private void handleSuccess(SysLog sysLog, Object result, Log log8) {
        sysLog.setStatus((byte) 1);
        if (log8.logResult()) {
            String res = JsonUtils.toJson(result);
            sysLog.setResult(truncate(res));
        }
    }

    private void handleError(SysLog sysLog, Exception e) {
        sysLog.setStatus((byte) 0);
        String stackTrace = ExceptionUtils.getStackTrace(e);
        sysLog.setErrorMsg(truncate(stackTrace));
    }

    private void finalizeAndSaveLog(SysLog sysLog) {
        long end = System.currentTimeMillis();
        sysLog.setEndTime(new Date(end));
        sysLog.setDuration(end - sysLog.getStartTime().getTime());

        try {
            sysLogService.save(sysLog);
        } catch (Exception e) {
            log.error("日志保存失败", e);
        }
    }

    // 工具方法：字符串截断
    private String truncate(String str) {
        if (str == null) return null;
        return str.length() <= 2000 ? str : str.substring(0, 2000);
    }
}
